<?php
class TaxonomyBehavior extends CActiveRecordBehavior
{
    public $name;
    public $taxonomy;
    public $module;
    public $objectAttribute = 'id';
    public $useActiveField = false;

    /**
     * Responds to {@link CActiveRecord::onAfterSave} event.
     * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}.
     * @param CModelEvent $event event parameter
     */
    public function afterSave($event)
    {
        $data = array();
        $objectId = $this->getOwner()->{$this->objectAttribute};
        if ($this->useActiveField) {
            $data = $this->getOwner()->{$this->name};
            if (!is_array($data) && !is_object($data)) {
                $data = array((int)$data);
            }
        }
        else {
            if (isset($_POST[$this->name])) {
                $data = $_POST[$this->name];
                if (!is_array($data) && !is_object($data)) {
                    $data = array((int)$data);
                }
            }
        }
        //delete old
        $sql = "DELETE FROM " . SITE_TABLE_PREFIX . "taxonomy_index t"
            . "\n USING " . SITE_TABLE_PREFIX . "taxonomy_term tt, " . SITE_TABLE_PREFIX . "taxonomy_vocabulary tv"
            . "\n WHERE tt.id = t.term_id AND tv.id = tt.v_id"
            . (is_numeric($this->taxonomy) ? "\n AND (tv.id = :id)" : "\n AND (tv.alias = :alias)")
            . "\n AND tv.module = :module AND t.object_id = :object_id";
        if (count($data)) {
            foreach ($data as $index => $val) {
                $data[$index] = CPropertyValue::ensureInteger($val);
            }
            $dataString = implode(',', $data);
            $sql .= "\n AND t.term_id NOT IN ({$dataString})";
        }
        $command = TermRelation::model()->getDbConnection()->createCommand($sql);
        if (is_numeric($this->taxonomy))
            $command->bindParam(":id", $this->taxonomy, PDO::PARAM_INT);
        else
            $command->bindParam(":alias", $this->taxonomy, PDO::PARAM_STR);
        $command->bindParam(":module", $this->module, PDO::PARAM_STR);
        $command->bindParam(":object_id", $objectId, PDO::PARAM_INT);
        $command->execute();

        //update/create
        if (count($data)) {
            foreach ($data as $val) {
                $relation = TermRelation::model()->findByAttributes(array('object_id' => $objectId, 'term_id' => $val));
                if (!is_object($relation)) {
                    $relation = new TermRelation();
                }
                $relation->object_id = $objectId;
                $relation->term_id = $val;
                if (!$relation->save()) {
                    Yii::log(CVarDumper::dumpAsString($relation->getErrors()), CLogger::LEVEL_ERROR);
                }
            }
        }
    }

    /**
     * find all vocavularies of current object
     *
     * @param int $state vocabulary state
     * @return array Vocabulary
     */
    public function getVocabularies($state = Vocabulary::STATE_ACTIVE)
    {
        $criteria = new CDbCriteria();
        $criteria->join = "INNER JOIN " . SITE_TABLE_PREFIX . "taxonomy_term tt ON tt.v_id = t.id"
            . "\n INNER JOIN " . SITE_TABLE_PREFIX . "taxonomy_index ti ON ti.term_id = tt.id";
        if (is_numeric($this->taxonomy))
            $criteria->compare('t.id', $this->taxonomy);
        else
            $criteria->compare('t.alias', $this->taxonomy);
        $criteria->compare('t.module', $this->module);
        $criteria->compare('t.state', $state);
        $objectId = (int)$this->getOwner()->{$this->objectAttribute};
        $criteria->compare('ti.object_id', $objectId);
        return Vocabulary::model()->findAll($criteria);
    }

    /**
     * get all terms of current object
     *
     * @param int $vstate vocabulary state
     * @param int $state term state
     * @return array Term
     */
    public function getTerms($vstate = Vocabulary::STATE_ACTIVE, $state = Term::STATE_ACTIVE)
    {
        $criteria = new CDbCriteria();
        $criteria->join = "INNER JOIN " . SITE_TABLE_PREFIX . "taxonomy_vocabulary tv ON tv.id = t.v_id"
            . "\n INNER JOIN " . SITE_TABLE_PREFIX . "taxonomy_index ti ON ti.term_id = t.id";
        if (is_numeric($this->taxonomy))
            $criteria->compare('tv.id', $this->taxonomy);
        else
            $criteria->compare('tv.alias', $this->taxonomy);
        $criteria->compare('tv.module', $this->module);
        $criteria->compare('tv.state', $vstate);
        $criteria->compare('t.state', $state);
        $objectId = (int)$this->getOwner()->{$this->objectAttribute};
        if ($objectId)
            $criteria->compare('ti.object_id', $objectId);
        return Term::model()->findAll($criteria);
    }

    /**
     * find object_id by term
     *
     * @param string $module
     * @param string $taxonomy
     * @param string|int|array $term
     * @return array
     */
    public function findObjectByTerm($module, $taxonomy, $term = null)
    {
        $condition = 'tv.module = :module AND (tv.id = :name)';
        $params = array(':module' => $module, ':name' => $taxonomy);
        if ($term !== null) {
            if (is_array($term)) {
                foreach ($term as $index => $val) {
                    $term[$index] = $this->getOwner()->getDbConnection()->quoteValue($val);
                }
                $term = implode(',', $term);
                $condition .= ' AND (tt.id IN (' . $term . '))';
            }
            else {
                $condition .= ' AND (tt.id = :term)';
                $params[':term'] = $term;
            }
        }
        /** @var CDbCommand */
        $command = $this->getOwner()->getDbConnection()->createCommand();
        return $command->select('ti.*')
            ->from(SITE_TABLE_PREFIX . 'taxonomy_index ti')
            ->join(SITE_TABLE_PREFIX . 'taxonomy_term tt', 'tt.id=ti.term_id')
            ->join(SITE_TABLE_PREFIX . 'taxonomy_vocabulary tv', 'tv.id=tt.v_id')
            ->where($condition, $params)
            ->order('ti.ordering')
            ->queryAll();
    }

    /**
     * add scope filter object by  terms
     *
     * @param string $module
     * @param string $taxonomy
     * @param string|int|array $term
     * @return CActiveRecord
     */
    public function term($module, $taxonomy, $term = null)
    {
        static $count;
        if ($count === null) $count = 1;
        else $count++;
        $suffix = '_' . $count;
        $condition = 'tv' . $suffix . '.module = :module' . $suffix;
        $params = array(':module' . $suffix => $module);
        if (is_numeric($taxonomy)) {
            $condition .= ' AND (tv' . $suffix . '.id = :id' . $suffix . ')';
            $params[':id' . $suffix] = $taxonomy;
        }
        else {
            $condition .= ' AND (tv' . $suffix . '.alias = :alias' . $suffix . ')';
            $params[':alias' . $suffix] = $taxonomy;
        }
        if ($term !== null) {
            if (is_array($term)) {
                $termId = array();
                $termAlias = array();
                foreach ($term as $index => $val) {
                    if (is_numeric($val))
                        $termId[$index] = $this->getOwner()->getDbConnection()->quoteValue($val);
                    else
                        $termAlias[$index] = $this->getOwner()->getDbConnection()->quoteValue($val);
                }
                if (count($termId)) {
                    $termId = implode(',', $termId);
                    $condition .= ' AND (tt' . $suffix . '.id IN (' . $termId . '))';
                }
                if (count($termAlias)) {
                    $termAlias = implode(',', $termAlias);
                    $condition .= ' AND (tt' . $suffix . '.alias IN (' . $termAlias . '))';
                }
            }
            else {
                if (is_numeric($term)) {
                    $condition .= ' AND (tt' . $suffix . '.id = :term' . $suffix . ')';
                    $params[':term' . $suffix] = $term;
                }
                else {
                    $condition .= ' AND (tt' . $suffix . '.alias = :term' . $suffix . ')';
                    $params[':term' . $suffix] = $term;
                }
            }
        }
        /** @var CDbCommand */
        $command = $this->getOwner()->getDbConnection()->createCommand();
        $condition = $command->select('ti' . $suffix . '.object_id')
            ->from(SITE_TABLE_PREFIX . 'taxonomy_index ti' . $suffix)
            ->join(SITE_TABLE_PREFIX . 'taxonomy_term tt' . $suffix, 'tt' . $suffix . '.id=ti' . $suffix . '.term_id')
            ->join(SITE_TABLE_PREFIX . 'taxonomy_vocabulary tv' . $suffix, 'tv' . $suffix . '.id=tt' . $suffix . '.v_id')
            ->where($condition, $params)
            ->text;

        $criteria = new CDbCriteria();
        $criteria->condition = 't.id IN (' . $condition . ')';
        $criteria->params = $params;
        /** @var CDbCriteria*/
        $this->getOwner()->getDbCriteria()->mergeWith($criteria);
        return $this->getOwner();
    }
}
